#include "stdafx.h"
#include "Texture2DManipulator.h"

namespace Cvekas {

Texture2DManipulator::Texture2DManipulator(Texture2DPtr texture, bool use_buffer)
	: 
use_buffer(use_buffer), 
buffer(NULL),
texture(texture)
{
	LOG(FORMAT("Constructing manipulator for texture %s", % texture->getName()));
	if(!texture->isLockable())
		throw EXCEPTION("Can't create manipulator for non-lockable texture");

	if(!(texture->getFormat() == D3DFMT_A8R8G8B8 || texture->getFormat() == D3DFMT_X8R8G8B8))
		throw EXCEPTION("Only D3DFMT_A8R8G8B8 and D3DFMT_X8R8G8B8 is supported for manipulation");

	if(texture->isDynamic() && !use_buffer)
		throw EXCEPTION("Dynamic textures must use buffer");

	if(!texture->isDynamic() && use_buffer)
		LOG("Using intermediate buffer for texture in managed pool. Might be unnecessary");

	D3DSURFACE_DESC desc = texture->getTopLevelDescription();
	height = desc.Height;
	
	//TODO: investigate if pitch might change between locks. Change pitch calculation if yes
	locked = texture->lock(true);
	pitch = locked.pitch;
	if(use_buffer)
	{
		buffer = new byte[height * pitch];
		memcpy(buffer, locked.data, height * pitch);
	}
	texture->unlock();
}

Texture2DManipulator::~Texture2DManipulator()
{
	if(texture->isLocked())
	{
		LOG("Texture still locked on destruction of manipulator. Did you forget to apply() changes?");
		unlock();
	}
	if(buffer)
		delete[] buffer;
	LOG(FORMAT("Manipulator for texture %s destroyed", % texture->getName()));
}

void Texture2DManipulator::setPixel(uint x, uint y, const Color& color)
{
	int address = pitch * y + x * 4;
	if(buffer)
	{
		D3DCOLOR* pixel = reinterpret_cast<D3DCOLOR*>(&buffer[address]);
		*pixel = (D3DCOLOR)color;
	}
	else
	{
		if(!texture->isLocked())
			lock();
		char* data = static_cast<char*>(locked.data);
		D3DCOLOR* pixel = reinterpret_cast<D3DCOLOR*>(&data[address]);
		*pixel = (D3DCOLOR)color;
	}
}

Color Texture2DManipulator::getPixel(uint x, uint y)
{
	int address = pitch * y + x * 4;
	if(buffer)
	{
		D3DCOLOR* pixel = reinterpret_cast<D3DCOLOR*>(&buffer[address]);
		return Color(*pixel);
	}
	else
	{
		if(!texture->isLocked()) lock();
		char* data = static_cast<char*>(locked.data);
		D3DCOLOR* pixel = reinterpret_cast<D3DCOLOR*>(&data[address]);
		return Color(*pixel);
	}
}

void Texture2DManipulator::apply()
{
	if(buffer)
	{
		lock();
		memcpy(locked.data, buffer, height * pitch);
		unlock();
	}
	else
	{
		if(texture->isLocked())
			unlock();
	}
}

void Texture2DManipulator::lock()
{
	locked = texture->lock(false);
}

void Texture2DManipulator::unlock()
{
	texture->unlock();
}

} // namespace
